package references

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 获取静态字段
type GET_STATIC struct{ base.Index16Instruction }

// getstatic需要一个操作数，16字节，可以在常量池定位到一个字段的符号引用
func (self *GET_STATIC) Execute(frame *runtime_area.Frame) {
	// 获取当前类的运行时常量池
	constantPool := frame.Method().Class().ConstantPool()
	// 从常量池获取字段的符号引用
	fieldRef := constantPool.GetConstant(self.Index).(*heap.FieldRef)
	// 根据符号引用获取直接引用
	field := fieldRef.ResolveField()
	// 这个字段所属的类
	class := field.Class()

	// 如果类还没有被初始化，先进行初始化
	if !class.InitStart(){
		// 中断当前指令，转而去执行clinit方法，所以当前栈帧要回退到上一条指令。clinit方法返回时，要重新执行这条指令
		// 其实这个clinit方法，可以在类加载的时候就执行，包括在jvm规范中描述的都是在类加载的时候执行
		// 不过Hotspot虚拟机是在实际用到的时候才执行clinit方法
		frame.RevertNextPc()
		base.InitClass(frame.Thread(),class)
		return
	}

	// 如果这个字段不是静态字段，抛出异常
	if !field.IsStatic() {
		panic("java.lang.IncompatibleClassChangeError")
	}

	descriptor:= field.Descriptor()
	slotId := field.SlotId()
	slots := class.StaticVars()
	stack := frame.OperandStack()
	switch descriptor[0] {
	case 'Z', 'B', 'C', 'S', 'I':
		// boolean，byte，char，short，int都当做int来处理
		stack.PushInt(slots.GetInt(slotId))
	case 'J':
		// long
		stack.PushLong(slots.GetLong(slotId))
	case 'F':
		// float
		stack.PushFloat(slots.GetFloat(slotId))
	case 'D':
		// double
		stack.PushDouble(slots.GetDouble(slotId))
	case 'L','[':
		// 引用类型和数组类型，描述符是不一样的，L和[
		stack.PushRef(slots.GetRef(slotId))
	}
}
